
local stringtools = require 'stringtools'
local alien = require 'alien'
local bit = require 'bit'
local ipairs, pairs, type, print, collectgarbage, error, pcall
    = ipairs, pairs, type, print, collectgarbage, error, pcall
local strformat
    = string.format

module (...)


DWORD = "ulong"
WORD = "ushort"

S_OK = 0
S_FALSE = 1

function MAKE_HRESULT(sev,fac,code)
  return bit.bor(bit.lshift(sev, 31), bit.lshift(fac, 16), code)
end
MAKE_SCODE = MAKE_HRESULT

local MAX_UPVALUES = 50

function definterface(def)
  local interface_name = def.name
  local Code = stringtools.builder()
  
  Code("local _M = ..." .. '\n'
    .. "local alien = require 'alien'" .. '\n'
    .. "local alien_buffer, alien_funcptr = alien.buffer, alien.funcptr" .. '\n')
  for i, method_info in ipairs(def) do
    Code("local _" .. interface_name .. "_" .. method_info.name .. '\n')
  end
  
  local ptr_len = alien.sizeof('pointer')
  local offset = 1
  local blocks = 1
  Code("local function init_1(vtbl)" .. '\n')
  for i, method_info in ipairs(def) do
    if (i % MAX_UPVALUES == 0) then
      blocks = blocks + 1
      Code("end" .. '\n'
        .. "local function init_" .. blocks .. "(vtbl)" .. '\n')
    end
    local name = "_" .. interface_name .. "_" .. method_info.name
    Code("  " .. name .. " = alien_funcptr(vtbl:get(" .. offset .. ", 'pointer'))" .. '\n'
      .. "  " .. name .. ":types{abi='stdcall';")
    for k,v in pairs(method_info) do
      if (k ~= 'name') then
        if type(k) == 'string' then
          Code(k .. '=')
        else
          Code('[' .. k .. ']=')
        end
        Code(strformat('%q;',v))
      end
    end
    Code("}" .. '\n')
    offset = offset + ptr_len
  end
  Code("end" .. '\n')
  Code ("local function init(this)" .. '\n'
    .. "  local vtbl = alien_buffer(alien_buffer(this):get(1, 'pointer'))" .. '\n')
  for i = 1, blocks do
    Code("  init_" .. i .. "(vtbl)" .. '\n')
  end
  Code("  init = nil" .. '\n'
    .. "end" .. '\n')
  for i, method_info in ipairs(def) do
    Code("function _M." .. interface_name .. "_" .. method_info.name .. "(...)" .. '\n'
      .. "  if init then  init(...)  end" .. '\n'
      .. "  return _" .. interface_name .. "_" .. method_info.name .. "(...)" .. '\n'
      .. "end" .. '\n')
  end
  --print(Code)
  Code:compile("@"..interface_name)(def.target)
end

WinProc_types = { ret = "long", abi = "stdcall"; "pointer", "uint", "uint", "long" }

GetLastError = alien.kernel32.GetLastError
GetLastError:types{ ret="uint", abi="stdcall" }

FormatMessageA = alien.kernel32.FormatMessageA
FormatMessageA:types{ ret="uint", abi="stdcall"; "uint", "pointer", "uint", "uint", "pointer", "uint", "pointer" }

LocalFree = alien.kernel32.LocalFree
LocalFree:types{ ret="pointer", abi="stdcall"; "pointer" }

FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100
FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000
FORMAT_MESSAGE_FROM_HMODULE = 0x00000800
FORMAT_MESSAGE_FROM_STRING = 0x00000400
FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200

LANG_NEUTRAL = 0

NEUTRAL_LANG_DEFAULT_SUBTYPE = 0x0400

local error_msg_buf = alien.buffer(alien.sizeof("pointer"))
local function LastErrorMessage()
  FormatMessageA(
    FORMAT_MESSAGE_ALLOCATE_BUFFER + FORMAT_MESSAGE_FROM_SYSTEM + FORMAT_MESSAGE_IGNORE_INSERTS,
    nil,
    GetLastError(),
    NEUTRAL_LANG_DEFAULT_SUBTYPE,
    error_msg_buf:topointer(),    
    0, nil)
  local ptr = error_msg_buf:get(1, 'pointer')
  local str = alien.tostring(ptr)
  LocalFree(ptr)
  return '[windows] ' .. str
end

function assert(v)
  if not (v and v ~= 0) then
    error(LastErrorMessage(),2)
  end
  return v
end

function assertNonZero(v)
  if (v == 0) then
    error(('%08x %s'):format(v, LastErrorMessage()),2)
  end
end

function assertNonNegative(v)
  if (v < 0) then
    error(('%08x %s'):format(v, LastErrorMessage()),2)
  end
end

GUID = alien.defstruct{
  {"Data1", "ulong"};
  {"Data2", "ushort"};
  {"Data3", "ushort"};
  {"Data4_1", "char"};
  {"Data4_2", "char"};
  {"Data4_3", "char"};
  {"Data4_4", "char"};
  {"Data4_5", "char"};
  {"Data4_6", "char"};
  {"Data4_7", "char"};
  {"Data4_8", "char"};
}
local guid = GUID:new()
function MAKE_GUID(d1, d2, d3, d4_1, d4_2, d4_3, d4_4, d4_5, d4_6, d4_7, d4_8)
  guid.Data1, guid.Data2, guid.Data3 = d1, d2, d3
  guid.Data4_1, guid.Data4_2, guid.Data4_3, guid.Data4_4 = d4_1, d4_2, d4_3, d4_4
  guid.Data4_5, guid.Data4_6, guid.Data4_7, guid.Data4_8 = d4_5, d4_6, d4_7, d4_8
  return guid():tostring(GUID.size)
end

MSG = alien.defstruct{
  {"hwnd", "pointer"};
  {"message", "uint"};
  {"wParam", "uint"};
  {"lParam", "long"};
  {"time", "ulong"};
  {"pt_X", "long"};
  {"pt_Y", "long"};
}

alien.kernel32.MultiByteToWideChar:types{
  abi="stdcall"; ret="int";
  "uint" --[[CodePage]], "ulong" --[[dwFlags]], "string" --[[lpMultiByteStr]], "int" --[[cbMultiByte]],
  "pointer" --[[lpWideCharStr]], "int" --[[cchWideChar]]}

CP_UTF8 = 65001
MB_PRECOMPOSED = 0x00000001
function MAKE_WIDE(str)
  local codePage = CP_UTF8
  local flags = 0
  local wide_len = alien.kernel32.MultiByteToWideChar(codePage, flags, str, #str+1, nil, 0)
  local buffer = alien.buffer(2 * wide_len)
  local res = alien.kernel32.MultiByteToWideChar(codePage, flags, str, #str+1, buffer, wide_len)
  return buffer
end

RECT = alien.defstruct{
  {"left", "long"};
  {"top", "long"};
  {"right", "long"};
  {"bottom", "long"};
}

GetClientRect = alien.user32.GetClientRect
GetClientRect:types{ abi="stdcall"; ret="int"; "pointer" --[[HWND]], "pointer" --[[RECT*]] }

GetWindowRect = alien.user32.GetWindowRect
GetWindowRect:types{ abi="stdcall"; ret="int"; "pointer" --[[HWND]], "pointer" --[[RECT*]] }

SetWindowPos = alien.user32.SetWindowPos
SetWindowPos:types{ abi="stdcall"; ret="int";
  "pointer" --[[HWND]], "pointer" --[[HWND insertAfter]], "int" --[[X]], "int" --[[Y]], "int" --[[cx]], "int" --[[cy]], "uint" --[[uFlags]] }

SWP_NOSIZE = 0x0001
SWP_NOMOVE = 0x0002
SWP_NOZORDER = 0x0004
SWP_NOREDRAW = 0x0008
SWP_NOACTIVATE = 0x0010
SWP_FRAMECHANGED = 0x0020
SWP_SHOWWINDOW = 0x0040
SWP_HIDEWINDOW = 0x0080
SWP_NOCOPYBITS = 0x0100
SWP_NOOWNERZORDER = 0x0200
SWP_NOSENDCHANGING = 0x0400

FillRect = alien.user32.FillRect
FillRect:types{ abi="stdcall"; ret="int";
  "pointer" --[[HDC]], "pointer" --[[const RECT*]], "pointer" --[[HBRUSH]] }

ShowWindow = alien.user32.ShowWindow
ShowWindow:types{ abi="stdcall"; ret="int" --[[BOOL]]; "pointer" --[[HWND]], "int" --[[nCmdShow]] }

GetDC = alien.user32.GetDC
GetDC:types{ ret="pointer"; "pointer" }

Sleep = alien.kernel32.Sleep
Sleep:types{ ret="void"; "ulong" --[[milliseconds]] }

GetStockObject = alien.gdi32.GetStockObject
GetStockObject:types{ ret = "pointer", abi = "stdcall"; "int" --[[fnObject]] }
  
WHITE_BRUSH     = 0
LTGRAY_BRUSH    = 1
GRAY_BRUSH      = 2
DKGRAY_BRUSH    = 3
BLACK_BRUSH     = 4
NULL_BRUSH      = 5
HOLLOW_BRUSH    = NULL_BRUSH

DestroyWindow = alien.user32.DestroyWindow
DestroyWindow:types{ ret = "int" --[[BOOL]], abi = "stdcall"; "pointer" --[[hwnd]] }

MINMAXINFO = alien.defstruct{
  {"ptReserved_x", "long"}; {"ptReserved_y", "long"};
  {"ptMaxSize_x", "long"}; {"ptMaxSize_y", "long"};
  {"ptMaxPosition_x", "long"}; {"ptMaxPosition_y", "long"};
  {"ptMinTrackSize_x", "long"}; {"ptMinTrackSize_y", "long"};
  {"ptMaxTrackSize_x", "long"}; {"ptMaxTrackSize_y", "long"};
}

ShowCursor = alien.user32.ShowCursor
ShowCursor:types{ ret="int", abi="stdcall"; "int" }

SetCursor = alien.user32.SetCursor
SetCursor:types{ ret="pointer"; abi="stdcall"; "pointer" }

LoadCursorA = alien.user32.LoadCursorA
LoadCursorA:types{ ret="pointer"; abi="stdcall"; "pointer", "pointer" }

local function numpointer(num)
  local buf = alien.buffer(alien.sizeof('long'))
  buf:set(1, num, 'long')
  return buf:get(1 + alien.sizeof('long') - alien.sizeof('pointer'), 'pointer')
end

IDC_ARROW = numpointer(32512)

MB_OK               = 0x00000000
MB_OKCANCEL         = 0x00000001
MB_ABORTRETRYIGNORE = 0x00000002
MB_YESNOCANCEL      = 0x00000003
MB_YESNO            = 0x00000004
MB_RETRYCANCEL      = 0x00000005

MB_ICONHAND         = 0x00000010
MB_ICONQUESTION     = 0x00000020
MB_ICONEXCLAMATION  = 0x00000030
MB_ICONASTERISK     = 0x00000040
  
MB_DEFBUTTON1       = 0x00000000
MB_DEFBUTTON2       = 0x00000100
MB_DEFBUTTON3       = 0x00000200

IDOK      = 1
IDCANCEL  = 2
IDABORT   = 3
IDRETRY   = 4
IDIGNORE  = 5
IDYES     = 6
IDNO      = 7

MessageBoxA = alien.user32.MessageBoxA
MessageBoxA:types{ ret = "long", abi = "stdcall"; "pointer", "string", "string", "long" }

GetSystemMetrics = alien.user32.GetSystemMetrics
GetSystemMetrics:types{ ret = "int"; "int" }

SM_CXSCREEN = 0
SM_CYSCREEN = 1

WNDCLASSEX = alien.defstruct{
  {"cbSize", "uint"};
  {"style", "uint"};
  {"lpfnWndProc", "callback"};
  {"cbClsExtra", "int"};
  {"cbWndExtra", "int"};
  {"hInstance", "pointer"};
  {"hIcon", "pointer"};
  {"hCursor", "pointer"};
  {"hbrBackground", "pointer"};
  {"lpszMenuName", "string"};
  {"lpszClassName", "string"};
  {"hIconSm", "pointer"};
}
  
PAINTSTRUCT = alien.defstruct{
  {"hdc", "pointer"};
  {"fErase", "int"};
  {"rcPaint_left", "long"};
  {"rcPaint_top", "long"};
  {"rcPaint_right", "long"};
  {"rcPaint_bottom", "long"};
  {"fRestore", "int"};
  {"fIncUpdate", "int"};
  --[[ {"rgbReserved", "byte[32]"}; ]]
}
PAINTSTRUCT.size = PAINTSTRUCT.size + 32   -- hack!
  
TRACKMOUSEEVENT = alien.defstruct{
  {"cbSize", "ulong"};
  {"dwFlags", "ulong"};
  {"hwndTrack", "pointer"};
  {"dwHoverTime", "ulong"};
}
  
CS_VREDRAW         = 0x0001
CS_HREDRAW         = 0x0002
CS_DBLCLKS         = 0x0008
CS_OWNDC           = 0x0020
CS_CLASSDC         = 0x0040
CS_PARENTDC        = 0x0080
CS_NOCLOSE         = 0x0200
CS_SAVEBITS        = 0x0800
CS_BYTEALIGNCLIENT = 0x1000
CS_BYTEALIGNWINDOW = 0x2000
CS_GLOBALCLASS     = 0x4000

CS_IME              = 0x00010000
  
WS_OVERLAPPED       = 0x00000000
WS_POPUP            = 0x80000000
WS_CHILD            = 0x40000000
WS_MINIMIZE         = 0x20000000
WS_VISIBLE          = 0x10000000
WS_DISABLED         = 0x08000000
WS_CLIPSIBLINGS     = 0x04000000
WS_CLIPCHILDREN     = 0x02000000
WS_MAXIMIZE         = 0x01000000
WS_CAPTION          = 0x00C00000 -- WS_BORDER | WS_DLGFRAME
WS_BORDER           = 0x00800000
WS_DLGFRAME         = 0x00400000
WS_VSCROLL          = 0x00200000
WS_HSCROLL          = 0x00100000
WS_SYSMENU          = 0x00080000
WS_THICKFRAME       = 0x00040000
WS_GROUP            = 0x00020000
WS_TABSTOP          = 0x00010000
  
WS_MINIMIZEBOX      = 0x00020000
WS_MAXIMIZEBOX      = 0x00010000  
  
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED     +
                             WS_CAPTION        +
                             WS_SYSMENU        +
                             WS_THICKFRAME     +
                             WS_MINIMIZEBOX    +
                             WS_MAXIMIZEBOX

CW_USEDEFAULT       = 0x80000000
  
SW_HIDE             = 0
SW_SHOWNORMAL       = 1
SW_NORMAL           = 1
SW_SHOWMINIMIZED    = 2
SW_SHOWMAXIMIZED    = 3
SW_MAXIMIZE         = 3
SW_SHOWNOACTIVATE   = 4
SW_SHOW             = 5
SW_MINIMIZE         = 6
SW_SHOWMINNOACTIVE  = 7
SW_SHOWNA           = 8
SW_RESTORE          = 9
SW_SHOWDEFAULT      = 10
SW_FORCEMINIMIZE    = 11
SW_MAX              = 11
  
WM_NULL           = 0x0000
WM_CREATE         = 0x0001
WM_DESTROY        = 0x0002
WM_MOVE           = 0x0003
WM_SIZE           = 0x0005
WM_ACTIVATE       = 0x0006
WM_SETFOCUS       = 0x0007
WM_KILLFOCUS      = 0x0008
WM_ENABLE         = 0x000A
WM_SETREDRAW      = 0x000B
WM_SETTEXT        = 0x000C
WM_GETTEXT        = 0x000D
WM_GETTEXTLENGTH  = 0x000E
WM_PAINT          = 0x000F
WM_CLOSE          = 0x0010
WM_QUIT           = 0x0012
WM_ERASEBKGND     = 0x0014
WM_SYSCOLORCHANGE = 0x0015
WM_SHOWWINDOW     = 0x0018
WM_WININICHANGE   = 0x001A
WM_GETMINMAXINFO  = 0x0024  
WM_NCMOUSEMOVE    = 0x00A0
WM_MOUSEMOVE      = 0x0200
WM_MOUSELEAVE     = 0x02A3  
WM_KEYDOWN        = 0x0100
WM_KEYUP          = 0x0101
WM_TIMER          = 0x0113
WM_NCLBUTTONDBLCLK = 0x00A3
WM_LBUTTONDOWN    = 0x0201
WM_LBUTTONUP      = 0x0202
WM_MBUTTONDOWN    = 0x0207
WM_MBUTTONUP      = 0x0208
WM_RBUTTONDOWN    = 0x0204
WM_RBUTTONUP      = 0x0205

HTERROR = (-2)
HTTRANSPARENT = (-1)
HTNOWHERE = 0
HTCLIENT = 1
HTCAPTION = 2
HTSYSMENU = 3
HTGROWBOX = 4
HTSIZE = HTGROWBOX
HTMENU = 5
HTHSCROLL = 6
HTVSCROLL = 7
HTMINBUTTON = 8
HTMAXBUTTON = 9
HTLEFT = 10
HTRIGHT = 11
HTTOP = 12
HTTOPLEFT = 13
HTTOPRIGHT = 14
HTBOTTOM = 15
HTBOTTOMLEFT = 16
HTBOTTOMRIGHT = 17
HTBORDER = 18
HTREDUCE = HTMINBUTTON
HTZOOM = HTMAXBUTTON
HTSIZEFIRST = HTLEFT
HTSIZELAST = HTBOTTOMRIGHT

MK_LBUTTON = 0x0001
MK_RBUTTON = 0x0002
MK_MBUTTON = 0x0003

WindowProc_types = { ret = "long", abi = "stdcall"; "pointer", "uint", "uint", "long" }

RegisterClassExA = alien.user32.RegisterClassExA
RegisterClassExA:types{ ret = "ushort", abi = "stdcall"; "pointer" }

DefWindowProcA = alien.user32.DefWindowProcA
DefWindowProcA:types(WindowProc_types)

CreateWindowExA = alien.user32.CreateWindowExA
CreateWindowExA:types{
  ret = "pointer", abi = "stdcall";
  "ulong", "string", "string", "ulong", "int", "int", "int", "int", "pointer", "pointer", "pointer", "pointer";
}

ShowWindow = alien.user32.ShowWindow
ShowWindow:types{ ret = "int", abi = "stdcall"; "pointer" --[[hwnd]], "int" --[[nCmdShow]] }

UpdateWindow = alien.user32.UpdateWindow
UpdateWindow:types{ ret = "int", abi = "stdcall"; "pointer" --[[hwnd]] }

PostQuitMessage = alien.user32.PostQuitMessage
PostQuitMessage:types{ ret = "void", abi = "stdcall"; "int" --[[nExitCode]] }

BeginPaint = alien.user32.BeginPaint
BeginPaint:types{ ret = "pointer"; "pointer", "pointer" }

EndPaint = alien.user32.EndPaint
EndPaint:types{ ret = "int"; "pointer", "pointer" }

TrackMouseEvent = alien.comctl32._TrackMouseEvent -- underscore deliberate!
TrackMouseEvent:types{ ret="int"; "pointer" }

TME_HOVER     = 0x00000001
TME_LEAVE     = 0x00000002
TME_NONCLIENT = 0x00000010
TME_QUERY     = 0x40000000
TME_CANCEL    = 0x80000000

SetWindowTextA = alien.user32.SetWindowTextA
SetWindowTextA:types{ ret="int"; "pointer", "string" }

GetDC = alien.user32.GetDC
GetDC:types{ ret="pointer"; "pointer" }

SetTimer = alien.user32.SetTimer
SetTimer:types{ ret="uint"; "pointer", "uint", "uint", "callback" }

TimerProc_types = { ret="void"; "pointer" --[[HWND]], "uint" --[[uMsg]], "uint" --[[idEvent]], DWORD --[[dwTime]] }

KillTimer = alien.user32.KillTimer
KillTimer:types{ ret="int" --[[BOOL]]; "pointer" --[[HWND]], "uint" --[[uIDEvent]] }

WS_EX_TOPMOST = 0x00000008

MSG = alien.defstruct{
  {"hwnd", "pointer"};
  {"message", "uint"};
  {"wParam", "uint"};
  {"lParam", "long"};
  {"time", "ulong"};
  {"pt_X", "long"};
  {"pt_Y", "long"};
}

GetMessageA = alien.user32.GetMessageA
GetMessageA:types{ ret = "int", abi = "stdcall"; "pointer", "pointer", "uint", "uint" }

TranslateMessage = alien.user32.TranslateMessage
TranslateMessage:types{ ret = "int", abi = "stdcall"; "pointer" }

DispatchMessageA = alien.user32.DispatchMessageA
DispatchMessageA:types{ ret = "int", abi = "stdcall"; "pointer" }

PeekMessageA = alien.user32.PeekMessageA
PeekMessageA:types{ ret="int", abi = "stdcall"; "pointer", "pointer", "uint", "uint", "uint" }
  
PM_REMOVE = 1

SRCCOPY             = 0x00CC0020 -- dest = source                  
SRCPAINT            = 0x00EE0086 -- dest = source OR dest          
SRCAND              = 0x008800C6 -- dest = source AND dest         
SRCINVERT           = 0x00660046 -- dest = source XOR dest         
SRCERASE            = 0x00440328 -- dest = source AND (NOT dest )  
NOTSRCCOPY          = 0x00330008 -- dest = (NOT source)            
NOTSRCERASE         = 0x001100A6 -- dest = (NOT src) AND (NOT dest)
MERGECOPY           = 0x00C000CA -- dest = (source AND pattern)    
MERGEPAINT          = 0x00BB0226 -- dest = (NOT source) OR dest    
PATCOPY             = 0x00F00021 -- dest = pattern                 
PATPAINT            = 0x00FB0A09 -- dest = DPSnoo                  
PATINVERT           = 0x005A0049 -- dest = pattern XOR dest        
DSTINVERT           = 0x00550009 -- dest = (NOT dest)              
BLACKNESS           = 0x00000042 -- dest = BLACK                   
WHITENESS           = 0x00FF0062 -- dest = WHITE                   

GetClientRect = alien.user32.GetClientRect
GetClientRect:types{ ret="int"; "pointer", "pointer" }

CreateCompatibleDC = alien.gdi32.CreateCompatibleDC
CreateCompatibleDC:types{ ret="pointer"; "pointer" }

CreateCompatibleBitmap = alien.gdi32.CreateCompatibleBitmap
CreateCompatibleBitmap:types{ ret="pointer"; "pointer", "int", "int" }

SelectObject = alien.gdi32.SelectObject
SelectObject:types{ ret="pointer"; "pointer", "pointer" }

DeleteObject = alien.gdi32.DeleteObject
DeleteObject:types{ ret="pointer"; "pointer" }

DeleteDC = alien.gdi32.DeleteDC
DeleteDC:types{ ret="pointer"; "pointer" }

BitBlt = alien.gdi32.BitBlt
BitBlt:types{ ret="int"; "pointer", "int", "int", "int", "int", "pointer", "int", "int", "long" }

IsZoomed = alien.user32.IsZoomed
IsZoomed:types{ ret="int"; "pointer" }

OPENFILENAME = alien.defstruct{ 
  {"lStructSize", DWORD};
  {"hwndOwner", "pointer"};
  {"hInstance", "pointer"};
  {"lpstrFilter", "string"};
  {"lpstrCustomFilter", "string"};
  {"nMaxCustFilter", DWORD};
  {"nFilterIndex", DWORD};
  {"lpstrFile", "pointer" --[[string buffer to receive selected file path]]};
  {"nMaxFile", DWORD};
  {"lpstrFileTitle", "pointer" --[[string buffer to receive file name only]]};
  {"nMaxFileTitle", DWORD};
  {"lpstrInitialDir", "string"};
  {"lpstrTitle", "string"};
  {"Flags", DWORD};
  {"nFileOffset", WORD};
  {"nFileExtension", WORD};
  {"lpstrDefExt", "string"};
  {"lCustData", "long" --[[LPARAM]]};
  {"lpfnHook", "pointer" --[[LPOFNHOOKPROC]]};
  {"lpTemplateName", "string"};
  {"pvReserved", "pointer"};
  {"dwReserved", DWORD};
  {"FlagsEx", DWORD};
}

MAX_PATH = 256

GetSaveFileNameA = alien.comdlg32.GetSaveFileNameA
GetSaveFileNameA:types{ ret="int" --[[bool]]; "pointer" --[[OPENFILENAME*]] }

GetOpenFileNameA = alien.comdlg32.GetOpenFileNameA
GetOpenFileNameA:types{ ret="int" --[[bool]]; "pointer" --[[OPENFILENAME*]] }

PIXELFORMATDESCRIPTOR = alien.defstruct{
  {"nSize", "ushort"};
  {"nVersion", "ushort"};
  {"dwFlags", "ushort"};
  {"iPixelType", "char"};
  {"cColorBits", "char"};
  {"cRedBits", "char"};
  {"cRedShift", "char"};
  {"cGreenBits", "char"};
  {"cGreenShift", "char"};
  {"cBlueBits", "char"};
  {"cBlueShift", "char"};
  {"cAlphaBits", "char"};
  {"cAlphaShift", "char"};
  {"cAccumBits", "char"};
  {"cAccumRedBits", "char"};
  {"cAccumGreenBits", "char"};
  {"cAccumBlueBits", "char"};
  {"cAccumAlphaBits", "char"};
  {"cDepthBits", "char"};
  {"cStencilBits", "char"};
  {"cAuxBuffers", "char"};
  {"iLayerType", "char"};
  {"bReserved", "char"};
  {"dwLayerMask", "ushort"};
  {"dwVisibleMask", "ushort"};
  {"dwDamageMask", "ushort"};
}

PFD_DOUBLEBUFFER = 0x00000001
PFD_STEREO = 0x00000002
PFD_DRAW_TO_WINDOW = 0x00000004
PFD_DRAW_TO_BITMAP = 0x00000008
PFD_SUPPORT_GDI = 0x00000010
PFD_SUPPORT_OPENGL = 0x00000020
PFD_GENERIC_FORMAT = 0x00000040
PFD_NEED_PALETTE = 0x00000080
PFD_NEED_SYSTEM_PALETTE = 0x00000100
PFD_SWAP_EXCHANGE = 0x00000200
PFD_SWAP_COPY = 0x00000400
PFD_SWAP_LAYER_BUFFERS = 0x00000800
PFD_GENERIC_ACCELERATED = 0x00001000
PFD_SUPPORT_DIRECTDRAW = 0x00002000   
  
PFD_TYPE_RGBA = 0
PFD_TYPE_COLORINDEX = 1
  
PFD_MAIN_PLANE = 0
PFD_OVERLAY_PLANE = 1
PFD_UNDERLAY_PLANE = -1

LoadLibraryA = alien.kernel32.LoadLibraryA
LoadLibraryA:types{ ret="pointer"; "string" }

FreeLibrary = alien.kernel32.FreeLibrary
FreeLibrary:types{ ret="int" --[[BOOL]]; "pointer" }

local dwmapi = LoadLibraryA("dwmapi")
if dwmapi then
  FreeLibrary(dwmapi)
  local DwmIsCompositionEnabled = alien.dwmapi.DwmIsCompositionEnabled
  DwmIsCompositionEnabled:types{ ret="long" --[[HRESULT]]; "ref int" --[[BOOL*]] }
  function UsingAero()
    local hresult, bool = DwmIsCompositionEnabled(0)
    return (bool == 1)
  end
else
  function UsingAero()
    return false
  end
end

SwapBuffers = alien.gdi32.SwapBuffers
SwapBuffers:types{ ret="int"; "pointer" }

ChoosePixelFormat = alien.gdi32.ChoosePixelFormat
ChoosePixelFormat:types{ ret="int"; "pointer", "pointer" }

SetPixelFormat = alien.gdi32.SetPixelFormat
SetPixelFormat:types{ ret="int"; "pointer", "int", "pointer" }

ValidateRect = alien.user32.ValidateRect
ValidateRect:types{ ret="void"; "pointer" --[[HWND]], "pointer" --[[RECT*]] }

VK_0	= 0x30
VK_1	= 0x31
VK_2	= 0x32
VK_3	= 0x33
VK_4	= 0x34
VK_5	= 0x35
VK_6	= 0x36
VK_7	= 0x37
VK_8	= 0x38
VK_9	= 0x39

VK_A	= 0x041
VK_B	= 0x042
VK_C	= 0x043
VK_D	= 0x044
VK_E	= 0x045
VK_F	= 0x046
VK_G	= 0x047
VK_H	= 0x048
VK_I	= 0x049
VK_J	= 0x04A
VK_K	= 0x04B
VK_L	= 0x04C
VK_M	= 0x04D
VK_N	= 0x04E
VK_O	= 0x04F
VK_P	= 0x050
VK_Q	= 0x051
VK_R  = 0x052
VK_S	= 0x053
VK_T	= 0x054
VK_U	= 0x055
VK_V	= 0x056
VK_W	= 0x057
VK_X	= 0x058
VK_Y	= 0x059
VK_Z	= 0x05A

VK_LBUTTON      = 0x01
VK_RBUTTON      = 0x02
VK_CANCEL       = 0x03
VK_BACK         = 0x08
VK_TAB          = 0x09
VK_CLEAR        = 0x0c
VK_RETURN       = 0x0d
VK_SHIFT        = 0x10
VK_CONTROL      = 0x11   -- ctrl
VK_MENU         = 0x12	 -- alt
VK_PAUSE        = 0x13
VK_CAPITAL      = 0x14
VK_ESCAPE       = 0x1b
VK_SPACE        = 0x20

VK_PRIOR        = 0x21
VK_NEXT         = 0x22
VK_END          = 0x23
VK_HOME         = 0x24
VK_LEFT         = 0x25
VK_UP           = 0x26
VK_RIGHT        = 0x27
VK_DOWN         = 0x28

VK_PRINT        = 0x2a
VK_EXECUTE      = 0x2b
VK_SNAPSHOT     = 0x2c
VK_INSERT       = 0x2d
VK_DELETE       = 0x2e
VK_HELP         = 0x2f

VK_NUMPAD0      = 0x60
VK_NUMPAD1      = 0x61
VK_NUMPAD2      = 0x62
VK_NUMPAD3      = 0x63
VK_NUMPAD4      = 0x64
VK_NUMPAD5      = 0x65
VK_NUMPAD6      = 0x66
VK_NUMPAD7      = 0x67
VK_NUMPAD8      = 0x68
VK_NUMPAD9      = 0x69
VK_MULTIPLY     = 0x6A
VK_ADD          = 0x6B
VK_SEPARATER    = 0x6C
VK_SUBTRACT     = 0x6D
VK_DECIMAL      = 0x6E
VK_DIVIDE       = 0x6F

VK_F1           = 0x70
VK_F2           = 0x71
VK_F3           = 0x72
VK_F4           = 0x73
VK_F5           = 0x74
VK_F6           = 0x75
VK_F7           = 0x76
VK_F8           = 0x77
VK_F9           = 0x78
VK_F10          = 0x79
VK_F11          = 0x7a
VK_F12          = 0x7b
VK_F13          = 0x7c
VK_F14          = 0x7d
VK_F15          = 0x7e
VK_F16          = 0x7f
VK_F17          = 0x80
VK_F18          = 0x81
VK_F19          = 0x82
VK_F20          = 0x83
VK_F21          = 0x84
VK_F22          = 0x85
VK_F23          = 0x86
VK_F24          = 0x87

VK_NUMLOCK      = 0x90
VK_SCROLL       = 0x91

tagBITMAP = alien.defstruct{
  {"bmType", "long"};
  {"bmWidth", "long"};
  {"bmHeight", "long"};
  {"bmWidthBytes", "long"};
  {"bmPlanes", "ushort"};
  {"bmBitsPixel", "ushort"};
  {"bmBits", "pointer"};
}
